From 8f424d5702e08c919eb029fe20d43bc3f093c9e5 Mon Sep 17 00:00:00 2001
From: Daniel Kolesa <daniel@octaforge.org>
Date: Fri, 9 Dec 2022 00:04:34 +0100
Subject: [PATCH 1/3] scudo: add necessary plumbing

---
 Makefile                    | 31 ++++++++++++++--
 configure                   | 72 ++++++++++++++++++++++++++++++++++---
 src/env/__init_tls.c        |  1 +
 src/include/errno.h         |  8 +++++
 src/include/features.h      |  2 ++
 src/include/pthread.h       |  4 +++
 src/include/time.h          |  4 +++
 src/internal/atomic.h       |  4 +++
 src/internal/linux/futex.h  | 31 ++++++++++++++++
 src/internal/pthread_impl.h |  3 ++
 src/internal/syscall.h      |  4 +++
 src/malloc/calloc.c         |  4 +++
 src/malloc/libc_calloc.c    |  4 +++
 src/thread/pthread_create.c |  6 ++++
 14 files changed, 172 insertions(+), 6 deletions(-)
 create mode 100644 src/internal/linux/futex.h

diff --git a/Makefile b/Makefile
index e8cc443..1715810 100644
--- a/Makefile
+++ b/Makefile
@@ -17,16 +17,19 @@ includedir = $(prefix)/include
 libdir = $(prefix)/lib
 syslibdir = /lib
 
-MALLOC_DIR = mallocng
+MALLOC_DIR = scudo
 SRC_DIRS = $(addprefix $(srcdir)/,src/* src/malloc/$(MALLOC_DIR) crt ldso $(COMPAT_SRC_DIRS))
 BASE_GLOBS = $(addsuffix /*.c,$(SRC_DIRS))
+CPP_GLOBS = $(addsuffix /*.cpp,$(SRC_DIRS))
 ARCH_GLOBS = $(addsuffix /$(ARCH)/*.[csS],$(SRC_DIRS))
 BASE_SRCS = $(sort $(wildcard $(BASE_GLOBS)))
+CPP_SRCS = $(sort $(wildcard $(CPP_GLOBS)))
 ARCH_SRCS = $(sort $(wildcard $(ARCH_GLOBS)))
 BASE_OBJS = $(patsubst $(srcdir)/%,%.o,$(basename $(BASE_SRCS)))
+CPP_OBJS = $(patsubst $(srcdir)/%,%.o,$(basename $(CPP_SRCS)))
 ARCH_OBJS = $(patsubst $(srcdir)/%,%.o,$(basename $(ARCH_SRCS)))
 REPLACED_OBJS = $(sort $(subst /$(ARCH)/,/,$(ARCH_OBJS)))
-ALL_OBJS = $(addprefix obj/, $(filter-out $(REPLACED_OBJS), $(sort $(BASE_OBJS) $(ARCH_OBJS))))
+ALL_OBJS = $(addprefix obj/, $(filter-out $(REPLACED_OBJS), $(sort $(BASE_OBJS) $(CPP_OBJS) $(ARCH_OBJS))))
 
 LIBC_OBJS = $(filter obj/src/%,$(ALL_OBJS)) $(filter obj/compat/%,$(ALL_OBJS))
 LDSO_OBJS = $(filter obj/ldso/%,$(ALL_OBJS:%.o=%.lo))
@@ -45,10 +48,18 @@ CPPFLAGS =
 CFLAGS =
 CFLAGS_AUTO = -Os -pipe
 CFLAGS_C99FSE = -std=c99 -ffreestanding -nostdinc 
+CXXFLAGS =
+CXXFLAGS_AUTO = -Os -pipe
+CXXFLAGS_FSE = -std=c++17 -ffreestanding -nostdinc -nostdinc++ -fno-exceptions -fno-rtti -fvisibility=hidden
 
 CFLAGS_ALL = $(CFLAGS_C99FSE)
 CFLAGS_ALL += -D_XOPEN_SOURCE=700 -I$(srcdir)/arch/$(ARCH) -I$(srcdir)/arch/generic -Iobj/src/internal -I$(srcdir)/src/include -I$(srcdir)/src/internal -Iobj/include -I$(srcdir)/include
+
+CXXFLAGS_ALL = $(CXXFLAGS_FSE)
+CXXFLAGS_ALL += -D_XOPEN_SOURCE=700 -I$(srcdir)/arch/$(ARCH) -I$(srcdir)/arch/generic -Iobj/src/internal -I$(srcdir)/src/include -I$(srcdir)/src/internal -Iobj/include -I$(srcdir)/include
+
 CFLAGS_ALL += $(CPPFLAGS) $(CFLAGS_AUTO) $(CFLAGS)
+CXXFLAGS_ALL += $(CPPFLAGS) $(CXXFLAGS_AUTO) $(CXXFLAGS)
 
 LDFLAGS_ALL = $(LDFLAGS_AUTO) $(LDFLAGS)
 
@@ -117,6 +128,7 @@ obj/crt/Scrt1.o obj/crt/rcrt1.o: CFLAGS_ALL += -fPIC
 
 OPTIMIZE_SRCS = $(wildcard $(OPTIMIZE_GLOBS:%=$(srcdir)/src/%))
 $(OPTIMIZE_SRCS:$(srcdir)/%.c=obj/%.o) $(OPTIMIZE_SRCS:$(srcdir)/%.c=obj/%.lo): CFLAGS += -O3
+$(OPTIMIZE_SRCS:$(srcdir)/%.cpp=obj/%.o) $(OPTIMIZE_SRCS:$(srcdir)/%.cpp=obj/%.lo): CXXFLAGS += -O3
 
 MEMOPS_OBJS = $(filter %/memcpy.o %/memmove.o %/memcmp.o %/memset.o, $(LIBC_OBJS))
 $(MEMOPS_OBJS) $(MEMOPS_OBJS:%.o=%.lo): CFLAGS_ALL += $(CFLAGS_MEMOPS)
@@ -130,8 +142,17 @@ $(NOSSP_OBJS) $(NOSSP_OBJS:%.o=%.lo): CFLAGS_ALL += $(CFLAGS_NOSSP)
 $(CRT_OBJS): CFLAGS_ALL += -DCRT
 
 $(LOBJS) $(LDSO_OBJS): CFLAGS_ALL += -fPIC
+$(LOBJS) $(LDSO_OBJS): CXXFLAGS_ALL += -fPIC
+
+obj/src/malloc/scudo/crc32_hw.lo: CXXFLAGS_ALL += $(CXXFLAGS_CRC)
+
+ifeq (scudo,$(MALLOC_DIR))
+obj/src/malloc/calloc.lo: CFLAGS_ALL += -DLIBC_CALLOC_EXTERNAL
+obj/src/malloc/libc_calloc.lo: CFLAGS_ALL += -DLIBC_CALLOC_EXTERNAL
+endif
 
 CC_CMD = $(CC) $(CFLAGS_ALL) -c -o $@ $<
+CXX_CMD = $(CXX) $(CXXFLAGS_ALL) -c -o $@ $<
 
 # Choose invocation of assembler to be used
 ifeq ($(ADD_CFI),yes)
@@ -149,6 +170,9 @@ obj/%.o: $(srcdir)/%.S
 obj/%.o: $(srcdir)/%.c $(GENH) $(IMPH)
 	$(CC_CMD)
 
+obj/%.o: $(srcdir)/%.cpp $(GENH) $(IMPH)
+	$(CXX_CMD)
+
 obj/%.lo: $(srcdir)/%.s
 	$(AS_CMD)
 
@@ -158,6 +182,9 @@ obj/%.lo: $(srcdir)/%.S
 obj/%.lo: $(srcdir)/%.c $(GENH) $(IMPH)
 	$(CC_CMD)
 
+obj/%.lo: $(srcdir)/%.cpp $(GENH) $(IMPH)
+	$(CXX_CMD)
+
 lib/libc.so: $(LOBJS) $(LDSO_OBJS)
 	$(CC) $(CFLAGS_ALL) $(LDFLAGS_ALL) -nostdlib -shared \
 	-Wl,-e,_dlstart -o $@ $(LOBJS) $(LDSO_OBJS) $(LIBCC)
diff --git a/configure b/configure
index ca5cbc0..b1a7386 100755
--- a/configure
+++ b/configure
@@ -63,6 +63,7 @@ fail () { echo "$*" ; exit 1 ; }
 fnmatch () { eval "case \"\$2\" in $1) return 0 ;; *) return 1 ;; esac" ; }
 cmdexists () { type "$1" >/dev/null 2>&1 ; }
 trycc () { test -z "$CC" && cmdexists "$1" && CC=$1 ; }
+trycxx () { test -z "$CXX" && cmdexists "$1" && CXX=$1 ; }
 
 stripdir () {
 while eval "fnmatch '*/' \"\${$1}\"" ; do eval "$1=\${$1%/}" ; done
@@ -97,6 +98,20 @@ return 1
 fi
 }
 
+tryxflag () {
+printf "checking whether C++ compiler accepts %s... " "$2"
+echo "typedef int x;" > "$tmpc"
+if $CXX $CXXFLAGS_TRY $2 -c -o /dev/null "$tmpc" >/dev/null 2>&1 ; then
+printf "yes\n"
+eval "$1=\"\${$1} \$2\""
+eval "$1=\${$1# }"
+return 0
+else
+printf "no\n"
+return 1
+fi
+}
+
 tryldflag () {
 printf "checking whether linker accepts %s... " "$2"
 echo "typedef int x;" > "$tmpc"
@@ -120,6 +135,10 @@ CFLAGS_AUTO=
 CFLAGS_MEMOPS=
 CFLAGS_NOSSP=
 CFLAGS_TRY=
+CXXFLAGS_FSE=
+CXXFLAGS_AUTO=
+CXXFLAGS_CRC=
+CXXFLAGS_TRY=
 LDFLAGS_AUTO=
 LDFLAGS_TRY=
 OPTIMIZE_GLOBS=
@@ -142,7 +161,7 @@ static=yes
 wrapper=auto
 gcc_wrapper=no
 clang_wrapper=no
-malloc_dir=mallocng
+malloc_dir=scudo
 
 for arg ; do
 case "$arg" in
@@ -180,7 +199,9 @@ case "$arg" in
 AR=*) AR=${arg#*=} ;;
 RANLIB=*) RANLIB=${arg#*=} ;;
 CC=*) CC=${arg#*=} ;;
+CXX=*) CXX=${arg#*=} ;;
 CFLAGS=*) CFLAGS=${arg#*=} ;;
+CXXFLAGS=*) CXXFLAGS=${arg#*=} ;;
 CPPFLAGS=*) CPPFLAGS=${arg#*=} ;;
 LDFLAGS=*) LDFLAGS=${arg#*=} ;;
 CROSS_COMPILE=*) CROSS_COMPILE=${arg#*=} ;;
@@ -253,6 +274,21 @@ printf "no; compiler output follows:\n%s\n" "$output"
 exit 1
 fi
 
+printf "checking for C++ compiler... "
+trycxx ${CROSS_COMPILE}g++
+trycxx ${CROSS_COMPILE}c++
+printf "%s\n" "$CXX"
+test -n "$CXX" || { echo "$0: cannot find a C++ compiler" ; exit 1 ; }
+
+printf "checking whether C++ compiler works... "
+echo "typedef int x;" > "$tmpc"
+if output=$($CXX $CPPFLAGS $CXXFLAGS -c -o /dev/null "$tmpc" 2>&1) ; then
+printf "yes\n"
+else
+printf "no; compiler output follows:\n%s\n" "$output"
+exit 1
+fi
+
 #
 # Figure out options to force errors on unknown flags.
 #
@@ -353,6 +389,14 @@ tryflag CFLAGS_C99FSE -fexcess-precision=standard \
 || { test "$ARCH" = i386 && tryflag CFLAGS_C99FSE -ffloat-store ; }
 tryflag CFLAGS_C99FSE -frounding-math
 
+tryxflag CXXFLAGS_FSE -std=c++17
+tryxflag CXXFLAGS_FSE -nostdinc
+tryxflag CXXFLAGS_FSE -nostdinc++
+tryxflag CXXFLAGS_FSE -fno-exceptions
+tryxflag CXXFLAGS_FSE -fno-rtti
+tryxflag CXXFLAGS_FSE -ffreestanding || tryxflag CXXFLAGS_FSE -fno-builtin
+tryxflag CXXFLAGS_FSE -fvisibility=internal || tryxflag CXXFLAGS_FSE -fvisibility=hidden
+
 #
 # We may use the may_alias attribute if __GNUC__ is defined, so
 # if the compiler defines __GNUC__ but does not provide it,
@@ -380,6 +424,7 @@ fi
 # linked with such object files. Fix this.
 #
 tryflag CFLAGS_C99FSE -Wa,--noexecstack
+tryxflag CXXFLAGS_FSE -Wa,--noexecstack
 
 #
 # Check for options to disable stack protector, which needs to be
@@ -397,10 +442,16 @@ tryflag CFLAGS_NOSSP -fno-stack-protector
 #
 tryflag CFLAGS_MEMOPS -fno-tree-loop-distribute-patterns
 
+# enable the necessary instruction set for hardware crc32
+if test "$ARCH" = "x86_64"; then
+    tryxflag CXXFLAGS_CRC -mcrc32 || tryxflag CXXFLAGS_CRC -msse4.2
+fi
+test "$ARCH" = "aarch64" && tryflag CXXFLAGS_CRC -mcrc
+
 #
 # Enable debugging if requessted.
 #
-test "$debug" = yes && CFLAGS_AUTO=-g
+test "$debug" = yes && CFLAGS_AUTO=-g && CXXFLAGS_AUTO=-g
 
 #
 # Preprocess asm files to add extra debugging information if debug is
@@ -437,7 +488,8 @@ xno|x) printf "disabled\n" ; optimize=no ;;
 esac
 
 test "$optimize" = no || tryflag CFLAGS_AUTO -Os || tryflag CFLAGS_AUTO -O2
-test "$optimize" = yes && optimize="internal,malloc,string"
+test "$optimize" = no || tryflag CXXFLAGS_AUTO -Os || tryflag CXXFLAGS_AUTO -O2
+test "$optimize" = yes && optimize="internal,malloc,malloc/scudo,string"
 
 if fnmatch 'no|size' "$optimize" ; then :
 else
@@ -449,6 +501,7 @@ case "$optimize" in
 esac
 printf " $this"
 case "$this" in
+malloc/scudo) this=$this/*.cpp ;;
 */*.c) ;;
 */*) this=$this*.c ;;
 *) this=$this/*.c ;;
@@ -461,6 +514,7 @@ fi
 
 # Always try -pipe
 tryflag CFLAGS_AUTO -pipe
+tryxflag CXXFLAGS_AUTO -pipe
 
 #
 # If debugging is disabled, omit frame pointer. Modern GCC does this
@@ -470,6 +524,7 @@ tryflag CFLAGS_AUTO -pipe
 if fnmatch '-g*|*\ -g*' "$CFLAGS_AUTO $CFLAGS" ; then :
 else
 tryflag CFLAGS_AUTO -fomit-frame-pointer
+tryxflag CXXFLAGS_AUTO -fomit-frame-pointer
 fi
 
 #
@@ -480,6 +535,8 @@ fi
 #
 tryflag CFLAGS_AUTO -fno-unwind-tables
 tryflag CFLAGS_AUTO -fno-asynchronous-unwind-tables
+tryxflag CXXFLAGS_AUTO -fno-unwind-tables
+tryxflag CXXFLAGS_AUTO -fno-asynchronous-unwind-tables
 
 #
 # Attempt to put each function and each data object in its own
@@ -491,6 +548,8 @@ tryflag CFLAGS_AUTO -fno-asynchronous-unwind-tables
 #
 tryflag CFLAGS_AUTO -ffunction-sections
 tryflag CFLAGS_AUTO -fdata-sections
+tryxflag CXXFLAGS_AUTO -ffunction-sections
+tryxflag CXXFLAGS_AUTO -fdata-sections
 
 #
 # On x86, make sure we don't have incompatible instruction set
@@ -511,7 +570,7 @@ fi
 # to start from a clean slate. So use -w if building with clang. Also
 # turn off a common on-by-default cast warning regardless of compiler.
 #
-test "$cc_family" = clang && tryflag CFLAGS_AUTO -w
+test "$cc_family" = clang && tryflag CFLAGS_AUTO -w && tryxflag CXXFLAGS_AUTO -w
 
 tryflag CFLAGS_AUTO -Wno-pointer-to-int-cast
 
@@ -784,11 +843,16 @@ libdir = $libdir
 includedir = $includedir
 syslibdir = $syslibdir
 CC = $CC
+CXX = $CXX
 CFLAGS = $CFLAGS
 CFLAGS_AUTO = $CFLAGS_AUTO
 CFLAGS_C99FSE = $CFLAGS_C99FSE
 CFLAGS_MEMOPS = $CFLAGS_MEMOPS
 CFLAGS_NOSSP = $CFLAGS_NOSSP
+CXXFLAGS = $CXXFLAGS
+CXXFLAGS_AUTO = $CXXFLAGS_AUTO
+CXXFLAGS_FSE = $CXXFLAGS_FSE
+CXXFLAGS_CRC = $CXXFLAGS_CRC
 CPPFLAGS = $CPPFLAGS
 LDFLAGS = $LDFLAGS
 LDFLAGS_AUTO = $LDFLAGS_AUTO
diff --git a/src/env/__init_tls.c b/src/env/__init_tls.c
index a93141e..6347577 100644
--- a/src/env/__init_tls.c
+++ b/src/env/__init_tls.c
@@ -21,6 +21,7 @@ int __init_tp(void *p)
 	td->detach_state = DT_JOINABLE;
 	td->tid = __syscall(SYS_set_tid_address, &__thread_list_lock);
 	td->locale = &libc.global_locale;
+	td->scudo_tsd = NULL;
 	td->robust_list.head = &td->robust_list.head;
 	td->sysinfo = __sysinfo;
 	td->next = td->prev = td;
diff --git a/src/include/errno.h b/src/include/errno.h
index 8ec4937..547e2af 100644
--- a/src/include/errno.h
+++ b/src/include/errno.h
@@ -3,6 +3,10 @@
 
 #include "../../include/errno.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #ifdef __GNUC__
 __attribute__((const))
 #endif
@@ -11,4 +15,8 @@ hidden int *___errno_location(void);
 #undef errno
 #define errno (*___errno_location())
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif
diff --git a/src/include/features.h b/src/include/features.h
index f17bd15..157f2de 100644
--- a/src/include/features.h
+++ b/src/include/features.h
@@ -3,7 +3,9 @@
 
 #include "../../include/features.h"
 
+#ifndef __cplusplus
 #define weak __attribute__((__weak__))
+#endif
 #define hidden __attribute__((__visibility__("hidden")))
 #define weak_alias(old, new) \
 	extern __typeof(old) new __attribute__((__weak__, __alias__(#old)))
diff --git a/src/include/pthread.h b/src/include/pthread.h
index 7167d3e..0107a70 100644
--- a/src/include/pthread.h
+++ b/src/include/pthread.h
@@ -3,6 +3,8 @@
 
 #include "../../include/pthread.h"
 
+#ifndef __cplusplus
+
 hidden int __pthread_once(pthread_once_t *, void (*)(void));
 hidden void __pthread_testcancel(void);
 hidden int __pthread_setcancelstate(int, int *);
@@ -26,4 +28,6 @@ hidden int __pthread_rwlock_trywrlock(pthread_rwlock_t *);
 hidden int __pthread_rwlock_timedwrlock(pthread_rwlock_t *__restrict, const struct timespec *__restrict);
 hidden int __pthread_rwlock_unlock(pthread_rwlock_t *);
 
+#endif /* __cplusplus */
+
 #endif
diff --git a/src/include/time.h b/src/include/time.h
index cbabde4..8eefd31 100644
--- a/src/include/time.h
+++ b/src/include/time.h
@@ -3,6 +3,8 @@
 
 #include "../../include/time.h"
 
+#ifndef __cplusplus
+
 hidden int __clock_gettime(clockid_t, struct timespec *);
 hidden int __clock_nanosleep(clockid_t, int, const struct timespec *, struct timespec *);
 
@@ -12,4 +14,6 @@ hidden struct tm *__localtime_r(const time_t *restrict, struct tm *restrict);
 
 hidden size_t __strftime_l(char *restrict, size_t, const char *restrict, const struct tm *restrict, locale_t);
 
+#endif /* __cplusplus */
+
 #endif
diff --git a/src/internal/atomic.h b/src/internal/atomic.h
index 96c1552..9c4e720 100644
--- a/src/internal/atomic.h
+++ b/src/internal/atomic.h
@@ -3,6 +3,8 @@
 
 #include <stdint.h>
 
+#ifndef __cplusplus
+
 #include "atomic_arch.h"
 
 #ifdef a_ll
@@ -330,4 +332,6 @@ static inline int a_clz_32(uint32_t x)
 }
 #endif
 
+#endif /* __cplusplus */
+
 #endif
diff --git a/src/internal/linux/futex.h b/src/internal/linux/futex.h
new file mode 100644
index 0000000..0e216dc
--- /dev/null
+++ b/src/internal/linux/futex.h
@@ -0,0 +1,31 @@
+#ifndef _INTERNAL_LINUX_FUTEX_H
+#define _INTERNAL_LINUX_FUTEX_H
+
+extern "C" {
+/* while at it, use the prefixed versions for mmap and so on */
+#include <sys/mman.h>
+/* must be first, otherwise the below include here would result in the syscall
+ * function in unistd being replaced by a macro into what would be invalid code
+ */
+#include <unistd.h>
+/* we include this here, so that linux.cpp in scudo does not use the unistd
+ * syscall definition but instead uses the macro that expands to raw calls
+ */
+#include "syscall.h"
+}
+
+#define mmap __mmap
+#define munmap __munmap
+#define mremap __mremap
+#define madvise __madvise
+#define mprotect __mprotect
+
+#define FUTEX_WAIT 0
+#define FUTEX_WAKE 1
+
+#define FUTEX_PRIVATE_FLAG 128
+
+#define FUTEX_WAIT_PRIVATE (FUTEX_WAIT | FUTEX_PRIVATE_FLAG)
+#define FUTEX_WAKE_PRIVATE (FUTEX_WAKE | FUTEX_PRIVATE_FLAG)
+
+#endif
diff --git a/src/internal/pthread_impl.h b/src/internal/pthread_impl.h
index de2b9d8..22b3923 100644
--- a/src/internal/pthread_impl.h
+++ b/src/internal/pthread_impl.h
@@ -56,6 +56,7 @@ struct pthread {
 	volatile int timer_id;
 	locale_t locale;
 	volatile int killlock[1];
+	void *scudo_tsd;
 	char *dlerror_buf;
 	void *stdio_locks;
 
@@ -187,6 +188,8 @@ hidden void __tl_lock(void);
 hidden void __tl_unlock(void);
 hidden void __tl_sync(pthread_t);
 
+hidden void __malloc_tsd_teardown(void *p);
+
 extern hidden volatile int __thread_list_lock;
 
 extern hidden volatile int __abort_lock[1];
diff --git a/src/internal/syscall.h b/src/internal/syscall.h
index d5f294d..53fe08d 100644
--- a/src/internal/syscall.h
+++ b/src/internal/syscall.h
@@ -58,6 +58,7 @@ hidden long __syscall_ret(unsigned long),
 #define __syscall_cp(...) __SYSCALL_DISP(__syscall_cp,__VA_ARGS__)
 #define syscall_cp(...) __syscall_ret(__syscall_cp(__VA_ARGS__))
 
+#ifndef __cplusplus
 static inline long __alt_socketcall(int sys, int sock, int cp, long a, long b, long c, long d, long e, long f)
 {
 	long r;
@@ -74,6 +75,7 @@ static inline long __alt_socketcall(int sys, int sock, int cp, long a, long b, l
 	(long)(a), (long)(b), (long)(c), (long)(d), (long)(e), (long)(f))
 #define __socketcall_cp(nm, a, b, c, d, e, f) __alt_socketcall(SYS_##nm, __SC_##nm, 1, \
 	(long)(a), (long)(b), (long)(c), (long)(d), (long)(e), (long)(f))
+#endif /* __cplusplus */
 
 /* fixup legacy 16-bit junk */
 
@@ -391,8 +393,10 @@ static inline long __alt_socketcall(int sys, int sock, int cp, long a, long b, l
 #define __sys_open_cp(...) __SYSCALL_DISP(__sys_open_cp,,__VA_ARGS__)
 #define sys_open_cp(...) __syscall_ret(__sys_open_cp(__VA_ARGS__))
 
+#ifndef __cplusplus
 hidden void __procfdname(char __buf[static 15+3*sizeof(int)], unsigned);
 
 hidden void *__vdsosym(const char *, const char *);
+#endif
 
 #endif
diff --git a/src/malloc/calloc.c b/src/malloc/calloc.c
index bf6bddc..6aa482c 100644
--- a/src/malloc/calloc.c
+++ b/src/malloc/calloc.c
@@ -32,6 +32,10 @@ weak_alias(allzerop, __malloc_allzerop);
 
 void *calloc(size_t m, size_t n)
 {
+#ifdef LIBC_CALLOC_EXTERNAL
+	if (!__malloc_replaced)
+		return __libc_calloc(m, n);
+#endif
 	if (n && m > (size_t)-1/n) {
 		errno = ENOMEM;
 		return 0;
diff --git a/src/malloc/libc_calloc.c b/src/malloc/libc_calloc.c
index d25eabe..3895c8c 100644
--- a/src/malloc/libc_calloc.c
+++ b/src/malloc/libc_calloc.c
@@ -1,4 +1,8 @@
+#ifndef LIBC_CALLOC_EXTERNAL
+
 #define calloc __libc_calloc
 #define malloc __libc_malloc
 
 #include "calloc.c"
+
+#endif
diff --git a/src/thread/pthread_create.c b/src/thread/pthread_create.c
index 6f187ee..2221554 100644
--- a/src/thread/pthread_create.c
+++ b/src/thread/pthread_create.c
@@ -68,6 +68,7 @@ _Noreturn void __pthread_exit(void *result)
 	}
 
 	__pthread_tsd_run_dtors();
+	__malloc_tsd_teardown(&self->scudo_tsd);
 
 	__block_app_sigs(&set);
 
@@ -315,6 +316,7 @@ int __pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict att
 	new->self = new;
 	new->tsd = (void *)tsd;
 	new->locale = &libc.global_locale;
+	new->scudo_tsd = NULL;
 	if (attr._a_detach) {
 		new->detach_state = DT_DETACHED;
 	} else {
@@ -391,3 +393,7 @@ fail:
 
 weak_alias(__pthread_exit, pthread_exit);
 weak_alias(__pthread_create, pthread_create);
+
+static void malloc_tsd_teardown(void *p) {}
+
+weak_alias(malloc_tsd_teardown, __malloc_tsd_teardown);
-- 
2.38.1

